package com.ziiwee.reduce.commons.utils;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

public class ReflectUtil {

    /**
     * 转换值类型（只包括基本类型、String 、Date 、BigDecimal 、Enum）
     *
     * @param type
     * @param value
     * @return
     */
    public static Object castValue(Class<?> type, Object value) {
        if (value == null) {
            return null;
        }
        if (String.class.equals(type)) {
            return value.toString();
        }
        if (Integer.class.equals(type) || int.class.equals(type)) {
            return Integer.parseInt(value.toString());
        }

        if (Date.class.equals(type)) {
            return DateUtil.parse(value.toString());
        }

        if (Boolean.class.equals(type) || boolean.class.equals(type)) {
            return Boolean.parseBoolean(value.toString()) || "1".equals(value.toString());
        }

        if (BigDecimal.class.equals(type)) {
            return new BigDecimal(value.toString());
        }

        if (Enum.class.equals(type.getSuperclass())) {
            for (Object o : type.getEnumConstants()) {
                if (Objects.equals(o.toString(), value.toString())) {
                    return o;
                }
            }
        }

        if (Long.class.equals(type) || long.class.equals(type)) {
            return Long.parseLong(value.toString());
        }
        if (Float.class.equals(type) || float.class.equals(type)) {
            return Float.parseFloat(value.toString());
        }
        if (Double.class.equals(type) || double.class.equals(type)) {
            return Double.parseDouble(value.toString());
        }
        if (Byte.class.equals(type) || byte.class.equals(type)) {
            return Byte.parseByte(value.toString());
        }
        if (Short.class.equals(type) || short.class.equals(type)) {
            return Short.parseShort(value.toString());
        }

        return null;
    }

    public static boolean isFromInterface(Class<?> type, Class<?> anInterface) {
        if (type == null || anInterface == null) {
            return false;
        }
        if (Objects.equals(type, anInterface)) {
            return true;
        }
        for (Class<?> typeInterface : type.getInterfaces()) {
            if (isFromInterface(typeInterface, anInterface)) {
                return true;
            }
        }
        if (type.getSuperclass() != null) {
            return isFromInterface(type.getSuperclass(), anInterface);
        }
        return false;
    }

    public static List<Class<?>> getTypeArgs(Class<?> type, Class<?> targetSuperType) {
        Class<?> superclass = type.getSuperclass();
        if (superclass != null && superclass != Object.class) {
            if (Objects.equals(superclass, targetSuperType)) {
                return getTypeArgs(type.getGenericSuperclass());
            }
            List<Class<?>> typeArgs = getTypeArgs(superclass, targetSuperType);
            if (typeArgs.size() > 0) {
                return typeArgs;
            }
        }
        Class<?>[] interfaces = type.getInterfaces();
        Type[] genericInterfaces = type.getGenericInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            Class<?> anInterface = interfaces[i];
            if (Objects.equals(anInterface, targetSuperType)) {
                return getTypeArgs(genericInterfaces[i]);
            }
            List<Class<?>> typeArgs = getTypeArgs(anInterface, targetSuperType);
            if (typeArgs.size() > 0) {
                return typeArgs;
            }
        }
        return new ArrayList<>();
    }

    public static List<Class<?>> getTypeArgs(Type type) {
        List<Class<?>> list = new ArrayList<>();
        if (type instanceof ParameterizedType) {
            Type[] args = ((ParameterizedType) type).getActualTypeArguments();
            for (Type arg : args) {
                try {
                    Class<?> aClass = Class.forName(arg.getTypeName());
                    list.add(aClass);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return list;
    }

}
